//
//  jscsh.c
//  jscsh
//
//  Created by Jonathan Hohle on 1/5/08.
//  Copyright hohle.net 2008. All rights reserved.
//
//  September 3rd, 2008:
//      dialtone, Jun 20, 2008 - 
//         Better input handling (http://code.google.com/p/jscsh/issues/detail?id=1)
//
//  Parts Copyright (C) Apple Computer
// -*- mode: c++; c-basic-offset: 4 -*-  2


#include <JavaScriptCore/JavaScriptCore.h>

#include <stdio.h>
#include <getopt.h>
#include "jscsh.h"
#include "util.h"

#include "JSSHell.h"
#include "IO.h"
#include "Dir.h"
#include "Socket.h"

#define LEN 4000

//#if HAVE(READLINE)
//char* line = readline(stdin);
//if (!line)
//break;
//if (line[0])
//add_history(line);
//free(line);
//#else


void printCLIHelp(int argc, char* argv[])
{
    printf("usage: %s [-r script] [script]\n", argv[0]);
    printf("\n"
           " --require, -r filename   load a JavaScript file into the global\n"
           "                          context before running a script or\n"
           "                          presenting a prompt\n"
           "\n"
           "If a script is provided, it will be evaluated and the interpretter\n"
           "will exit immediately. If no script is provided, an interactive\n"
           "shell will be started\n");
}

void createGlobalJSFunction(const char* name,
                            JSContextRef context,
                            JSObjectRef globalObject,
                            JSObjectCallAsFunctionCallback functionPointer)
{
    JSStringRef functionNameString = JSStringCreateWithUTF8CString(name);
    JSObjectSetProperty(context,
                        globalObject,
                        functionNameString,
                        JSObjectMakeFunctionWithCallback(context, functionNameString, functionPointer),
                        kJSPropertyAttributeNone,
                        NULL);
    JSStringRelease(functionNameString);
}


int main(int argc, char *argv[])
{

    JSGlobalContextRef context = JSGlobalContextCreate(NULL);
    JSObjectRef contextGlobalObject = JSContextGetGlobalObject(context);

    JSShell_extendHost(context, contextGlobalObject);
    IO_extendHost(context, contextGlobalObject);
    Dir_extendHost(context, contextGlobalObject);
    File_extendHost(context, contextGlobalObject);
    Socket_extendHost(context, contextGlobalObject);
    
    static struct option longOptions[] = {
        {"require", required_argument, NULL, 'r'},
        {NULL, 0, NULL, 0}
    };
    
    int ch;
    while ((ch = getopt_long(argc, argv, "r:", longOptions, NULL)) != -1)
    {
        JSStringRef stringRef;
        JSValueRef arguments[1];
        JSValueRef exception;
        switch (ch)
        {
            case 'r':
                stringRef = JSStringCreateWithUTF8CString(optarg);
                arguments[0] = JSValueMakeString(context, stringRef);
                JSShell_load(context, NULL, NULL, 1, arguments, &exception);
                break;
            case 'h':
            case 0:
            default:
                printCLIHelp(argc, argv);
                exit(0);
                break;
        }
    }
    
    argc -= optind;
    argv += optind;

    if (argc > 0)
    {
        JSStringRef stringRef = JSStringCreateWithUTF8CString(argv[0]);
        JSValueRef arguments[] = {JSValueMakeString(context, stringRef)};
        JSValueRef exception;
        
        if (argc > 1)
        {
            JSValueRef argumentsArray = JSValueMakeArray(context);
            JSObjectRef argumentsArrayObject = JSValueToObject(context, argumentsArray, NULL);
            JSStringRef argumentsString = JSStringCreateWithUTF8CString("arguments");
            JSObjectSetProperty(context,
                                contextGlobalObject,
                                argumentsString,
                                argumentsArray,
                                kJSPropertyAttributeNone,
                                NULL);
            JSStringRelease(argumentsString);
            JSStringRef pushString = JSStringCreateWithUTF8CString("push");
            JSObjectRef pushMethod = JSValueToObject(context,
                                                     JSObjectGetProperty(context,
                                                                         argumentsArrayObject,
                                                                         pushString,
                                                                         NULL),
                                                     NULL);
            JSStringRelease(pushString);
            
            unsigned int i = 1;
            for (i = 1; i < argc; ++i)
            {
                JSStringRef argumentString = JSStringCreateWithUTF8CString(argv[i]);
                JSValueRef argumentValue = JSValueMakeString(context, argumentString);
                JSStringRelease(argumentString);
                JSValueRef arguments[] = {argumentValue};
                
                JSObjectCallAsFunction(context,
                                       pushMethod,
                                       argumentsArrayObject,
                                       1,
                                       arguments,
                                       &exception);
            }
        }
        
        JSShell_load(context, NULL, NULL, 1, arguments, &exception);
        exit(0);
    }
    
    
    char* prompt = "js> ";
    
    bool keep_running = true;
    while (keep_running)
    {
        printf(prompt);
        char line[LEN];
        int c, i;

        i = 0;
        while ((c = getchar()) != EOF || i < LEN) {
            // FIXME: Should we also break on \r? 
            if (c == '\n')
                break;
            if (c == -1) {
                keep_running = false;
                break;
            }
            line[i] = c;
            ++i;
        }
        line[i] = '\0';
        JSStringRef stringRef = JSStringCreateWithUTF8CString(line);
        JSValueRef exception = NULL;

        if (JSCheckScriptSyntax(context,
                                stringRef,
                                NULL,
                                0,
                                &exception))
        {
            JSValueRef returnValue = JSEvaluateScript(context,
                                                      stringRef,
                                                      NULL,
                                                      NULL,
                                                      1,
                                                      &exception);
            
            if (returnValue && !exception)
            {
                if (JSValueGetType(context, returnValue) != kJSTypeUndefined)
                {
                    JSValueRef arguments[] = {returnValue};
                    JSShell_print(context, NULL, NULL, 1, arguments, &exception);
                }
                else
                {
                    printf("\n");
                }
            }
            else if (exception)
            {
                JSValueRef arguments[] = {exception};
                JSShell_print(context, NULL, NULL, 1, arguments, &exception);
                exception = NULL;
            }
        }
        else // if there was a parse error
        {
            if (exception)
            {
                JSValueRef arguments[] = {exception};
                JSShell_print(context, NULL, NULL, 1, arguments, &exception);
                exception = NULL;
            }
            
        }

    }
    JSGlobalContextRelease(context);
    JSGarbageCollect(context);
    return 0;
}
